#pragma once

#ifndef __GC_TYPES_H__
#define __GC_TYPES_H__
#include <vector>
#include <map>

#include "hw_app_config.h"
#include "hw_app_log.h"
#include "hw_cv_type.h"

typedef enum GC_YOLO_VERSION {
    YOLO_UNKNOWN = -1,
    YOLO_V5 = 0,
    YOLO_V7 = 1,
    YOLO_V5_ONLY_PLATE = 2,
    YOLO_V5_WITH_PLATE = 3,
    YOLO_V5_ONLY_PLATE_CAR = 4, // 3分类 车牌、双层车牌、汽车（car/truck/bus）
} GC_YOLO_VERSION;

typedef enum GC_CAMERA_MODEL {
    MODEL_UNKNOWN = 0,
    MODEL_M2141 = 1,
    MODEL_M2241 = 2,
    MODEL_C6650 = 3,
    MODEL_M6741 = 4,
    MODEL_C2150 = 5,
    MODEL_GUARD_V2 = 6, // 护栏二代
    MODEL_M2281 = 7,
    MODEL_X2241 = 8,
    MODEL_C6620 = 9,
} GC_CAMERA_MODEL;

typedef enum GC_CAMERA_MODE {
    MODE_UNKNOWN = 0,
    MODE_WK = 1,
    MODE_ZL = 2,
} GC_CAMERA_MODE;

typedef enum GC_CAMERA_POS {
    POS_UNKNOWN = 0,
    POS_LEFT = 1,
    POS_RIGHT = 2,
    POS_UP = 3,
    POS_DOWN = 4,
} GC_CAMERA_POS;

typedef enum GC_CAMERA_STYLE {
    STYLE_UNKNOWN = 0,
    STYLE_ZHENG = 1,
    STYLE_XIE = 2,
    STYLE_CHUI = 3,
    STYLE_BAN = 4,
} GC_CAMERA_STYLE;

typedef enum GC_CAMERA_STATE {
    STATE_UNKNOWN = 0,
    STATE_INIT = 1,
    STATE_CONFIG = 2,
    STATE_OFFLINE = 3,
    STATE_ONLINE = 4,
    STATE_DEBUG = 5,
} GC_CAMERA_STATE;

typedef struct GC_BERTH {
    std::string name;
    std::vector<int> roi;
} GC_BERTH;

typedef enum GC_PLATE_COLOR {
    PLATE_COLOR_BLUE = 1,
    PLATE_COLOR_YELLOW = 2,
    PLATE_COLOR_BLACK = 3,
    PLATE_COLOR_WHITE = 4,
    PLATE_COLOR_GREEN = 5,
} GC_PLATE_COLOR;

typedef enum GC_CACHE_POSITION {
    MEMORY = 0,
    DISK = 1,
} GC_CACHE_POSITION;

// 运行算法类型，目前是互斥的，同时只能跑一种算法
typedef enum GC_ALG_TYPE {
    ROAD_PARKING = 0, // 路内泊车
    SEMI_CLOSED_PARKING = 1, // 半封闭停车
    CLOSED_PARKING = 2, // 封闭停车
    ILLEGALE_PARKING = 3, // 违法停车
} GC_ALG_TYPE;

typedef enum GC_LOCAL_LPR_TYPE {
    NCNN = 0,
    MNN_CPU = 1,
    MNN_NNIE = 2,
} GC_LOCAL_LPR_TYPE;

typedef enum GC_PLATE_TYPE {
    SINGLE = 0, // 单层车牌
    DOUBLE = 1, // 双层车牌
} GC_PLATE_TYPE;

typedef struct GC_CAMERA_CONFIG {
    std::string sn;
    std::string ip;
    std::string name;
    std::vector<int> roi;
    GC_CAMERA_MODEL model;
    GC_CAMERA_MODE mode;
    GC_CAMERA_POS pos;
    GC_CAMERA_STYLE style;
    GC_CAMERA_STATE state;
    bool debug;
    std::vector<GC_BERTH> berthes;
    int rotation; // 图像需要旋转的角度
    std::string road;
    std::vector<GC_BERTH> alarm_roi;
    std::string area;
    GC_ALG_TYPE alg_type;
    int infer_image_quality; // 推理使用图片压缩质量，默认90

    // 仅前端用到
    std::time_t last_status_image_save_time; // 该预置位相机最新状态图保存时间
    bool has_new_state_image;
    std::string new_state_image_time_str;

    GC_CAMERA_CONFIG() {
        sn = "";
        ip = "";
        roi = std::vector<int>({400, 400, 800, 400, 800, 800, 400, 800});
        model = GC_CAMERA_MODEL::MODEL_UNKNOWN;
        mode = GC_CAMERA_MODE::MODE_WK;
        pos = GC_CAMERA_POS::POS_LEFT;
        style = GC_CAMERA_STYLE::STYLE_ZHENG;
        state = GC_CAMERA_STATE::STATE_ONLINE;
        debug = true;
        rotation = 0;
        road = "无名路";
        area = "gc";
        alg_type = GC_ALG_TYPE::ROAD_PARKING; // TODO 目前工具没有实现该配置，需要手动修改
        infer_image_quality = 90; // TODO 目前工具没有实现该配置，需要手动修改
        last_status_image_save_time = 0;
        has_new_state_image = false;
        new_state_image_time_str = "";
    }

    GC_CAMERA_CONFIG(const GC_CAMERA_CONFIG& o) {
        sn = o.sn;
        ip = o.ip;
        roi.assign(o.roi.begin(), o.roi.end());
        model = o.model;
        mode = o.mode;
        pos = o.pos;
        style = o.style;
        state = o.state;
        debug = o.debug;
        berthes.assign(o.berthes.begin(), o.berthes.end());
        rotation = o.rotation;
        road = o.road;
        alarm_roi.assign(o.alarm_roi.begin(), o.alarm_roi.end());
        area = o.area;
        alg_type = o.alg_type;
        infer_image_quality = o.infer_image_quality;
        last_status_image_save_time = o.last_status_image_save_time;
        has_new_state_image = o.has_new_state_image;
        new_state_image_time_str = o.new_state_image_time_str;
    }

    bool needRestart(const GC_CAMERA_CONFIG& newer) {
        return !ip.empty() && newer.ip != ip;
    }

} GC_CAMERA_CONFIG;

typedef struct GC_ALG_CONFIG {
    int default_yuv_img_w;
    int default_yuv_img_h;
    int subscribe_yuv_img_w;
    int subscribe_yuv_img_h;
    int subscribe_yuv_fps;
    float yolo_conf_thresh;
    std::string model_path;
    std::vector<int> classes;
    std::vector<int> plate_classes;
    std::vector<int> car_classes;
    
    int track_max_idle_obj; // 无行为目标最多保留帧数
    int track_max_obj; // 目标最多保留帧数，无论是否有待确定行为
    int track_max_idle_time; // 单位：秒 追踪最大空闲时间（距离最后一个目标时间大于该值则结束追踪）
    float track_same_plate_iou_thresh; // iou大于该值则认为是同一个车牌，只保留置信度最高的一个
    std::string priority_province_chinese; // 是否指定优先省份
    std::string priority_province_char; // 第二位地区字符
    int chinese_thres; // 优先省份阈值(1-100)
    int chinese_thres_for_char; // 根据第二位城市字母判断优先省份阈值(1-100)
    bool lpr_need_detect; // 车牌识别接口是否需要进行检测
    bool lpr_need_transformer; // 车牌识别接口是否需要进行仿射变换

    std::string cache_dir;
    GC_CACHE_POSITION cache_position; // GC_CACHE_POSITION
    int img_cache_size; // 图像缓存数量上限，内存缓存模式下不生效，没有重新初始IMG_CACHE

    GC_LOCAL_LPR_TYPE local_lpr_type; // 本地车牌识别采用算法类型
    std::string local_lpr_model_path; // 本地车牌识别nnie推理模型路径
    int lpr_img_w;
    int lpr_img_h;

    GC_ALG_CONFIG() {

        default_yuv_img_w = 1920;
        default_yuv_img_h = 1080;
        
        // wk
        subscribe_yuv_img_w = 1920;
        subscribe_yuv_img_h = 1080;
        
        // zl
        // subscribe_yuv_img_w = 1280;
        // subscribe_yuv_img_h = 720;
        subscribe_yuv_fps = 2;
        // yolo_conf_thresh = 0.4f * SAMPLE_SVP_NNIE_QUANT_BASE;
        yolo_conf_thresh = 0.4f;

        // classes = std::vector<int>({2, 5, 7, 80});
        classes = std::vector<int>({2, 7});
        car_classes = std::vector<int>({2, 7});

        lpr_need_detect = true;
        lpr_need_transformer = true;

        GC_YOLO_VERSION yolo_version = static_cast<GC_YOLO_VERSION>(YOLO_VERSION | GC_YOLO_VERSION::YOLO_V5_ONLY_PLATE_CAR);

        LOG_INFO("yolo_version=%d", yolo_version);

        switch (yolo_version) {
            case GC_YOLO_VERSION::YOLO_V5:
                // model_path = "./model/yolov5m_3519_yuv_rgb_80class_inst.wk";
                break;
            case GC_YOLO_VERSION::YOLO_V7:
                // model_path = "./model/yolov7_3519_yuv_rgb_80class_inst.wk";
                break;
            case GC_YOLO_VERSION::YOLO_V5_ONLY_PLATE:
                // model_path = "./model/lpd0616_yuv_rgb_leakyrelu.wk";
                // model_path = "./model/lpd0616_yuv_rgb.wk";
                classes = std::vector<int>({0});
                break;
            case GC_YOLO_VERSION::YOLO_V5_WITH_PLATE:
                // model_path = "./model/yolov5m_3519_yuv_rgb_81class_inst.wk";
                classes = std::vector<int>({2, 7, 80});
                plate_classes = std::vector<int>({80});
                break;
            case GC_YOLO_VERSION::YOLO_V5_ONLY_PLATE_CAR:
                model_path = "./model/yolov5m_3519_yuv_rgb_3class_inst_v6.2.wk";
                // model_path = "./model/yolov5_3519_yuv_rgb_fp16_cls3_s_norm_inst_v2.wk";
                // model_path = "./model/yolov5_3519_yuv_rgb_fp16_cls3_n_norm_inst_v2.wk";
                classes = std::vector<int>({GC_PLATE_TYPE::SINGLE, GC_PLATE_TYPE::DOUBLE, 2}); // 单层车牌、双层车牌、车辆
                plate_classes = std::vector<int>({0, 1});
                car_classes = std::vector<int>({2});
                lpr_need_detect = false;
                lpr_need_transformer = false;
                break;
            default:
                // model_path = "./model/yolov5m_3519_yuv_rgb_3class_inst_v6.2.wk";
                break;
        }

        track_max_idle_obj = subscribe_yuv_fps * 60;
        track_max_obj = subscribe_yuv_fps * 90;
        track_max_idle_time = 10;
        // track_min_x_move_distance = 20;
        track_same_plate_iou_thresh = 0.5;

        chinese_thres = 85;
        chinese_thres_for_char = 95;
        
        cache_dir = "/tmp/.AIPlus/";
        cache_position = GC_CACHE_POSITION::DISK;
        img_cache_size = 360;

        local_lpr_type = GC_LOCAL_LPR_TYPE::MNN_NNIE;
        // local_lpr_model_path = "./model/lpr_3519_u8_bgr_fp32_inst.wk";
        // local_lpr_model_path = "./model/lpr_3519_u8_bgr_fp16_norm_inst.wk";
        local_lpr_model_path = "./model/lpr_3519_u8_bgr_fp16_cls81_medium_norm_inst.wk";
        if (local_lpr_type == GC_LOCAL_LPR_TYPE::NCNN) {
            lpr_img_w = 100;
            lpr_img_h = 32;
        } else {
            lpr_img_w = 168;
            lpr_img_h = 48;
        }
    }

    void low_fps() {
        model_path = "./model/yolov5_3519_yuv_rgb_fp16_cls3_s_norm_inst_v2.wk";
        subscribe_yuv_fps = 1;
        // track_max_idle_obj = subscribe_yuv_fps * 30;
        // img_cache_size = 240;
    }

    void park_fps() {
        // subscribe_yuv_fps = 1;
        track_max_idle_obj = subscribe_yuv_fps * 15;
        // track_max_obj = subscribe_yuv_fps * 90;
        // img_cache_size = 240;
        track_max_idle_time = 5;
    }

    bool needRestart(const GC_ALG_CONFIG& newer) {
        return newer.default_yuv_img_w != default_yuv_img_w || newer.default_yuv_img_h != default_yuv_img_h || (cache_position == GC_CACHE_POSITION::MEMORY && newer.img_cache_size != img_cache_size);
    }

} GC_ALG_CONFIG;

typedef enum GC_ATTACHMENT_TYPE {
    ATTACH_TYPE_UNKNOWN = -1,
    ATTACH_TYPE_BIN = 1,
    ATTACH_TYPE_MODEL = 2,
    ATTACH_TYPE_LIB = 3,
    ATTACH_TYPE_CONFIG = 4,
} GC_ATTACHMENT_TYPE;

typedef struct GC_APP_ATTACHMENT {
    std::string name;
    std::string url;
    std::string tag;
    int upload_ts;
    GC_ATTACHMENT_TYPE attachment_type;
} GC_ATTACHMENT;

typedef struct GC_BASE_CONFIG {
    GC_ALG_CONFIG alg_config;
    bool upload_state_image;
    std::string middleware_host;
    int middleware_upload_image_period;
    std::string middleware_upload_image_api; // 上传状态图api
    std::string middleware_upload_inout_api; // 上传行为结果api
    std::string lpr_host; // set to string local when using local lpr
    std::string lpr_api;
    std::vector<GC_APP_ATTACHMENT> attachment_list;
    std::string ftp_host;

    GC_BASE_CONFIG() {
        upload_state_image = true;
        middleware_host = "";
        middleware_upload_image_period = 600; // unit: second
        middleware_upload_image_api = "/parking_collect_entry/newDeviceUpload/parkingStatus";
        middleware_upload_inout_api = "/parking_collect_entry/newDeviceUpload/carPass";
        lpr_host = "";
        lpr_api = "/lpr";
        ftp_host = "222.128.47.209:9001";
    }

    GC_BASE_CONFIG(const GC_BASE_CONFIG& o) {
        upload_state_image = o.upload_state_image;
        middleware_host = o.middleware_host;
        middleware_upload_image_api = o.middleware_upload_image_api;
        middleware_upload_image_period = o.middleware_upload_image_period;
        middleware_upload_inout_api = o.middleware_upload_inout_api;
        lpr_host = o.lpr_host;
        lpr_api = o.lpr_api;
        attachment_list.assign(o.attachment_list.begin(), o.attachment_list.end());
        ftp_host = o.ftp_host;
        alg_config = o.alg_config;
    }

    bool needRestart(const GC_BASE_CONFIG& newer) {
        return (!middleware_host.empty() && newer.middleware_host != middleware_host) || (!lpr_host.empty() && newer.lpr_host != lpr_host) || alg_config.needRestart(newer.alg_config);
    }

} GC_BASE_CONFIG;

typedef struct GC_APP_CONFIG {
    std::string hardware_id; // 相机硬件id，原始sn
    bool has_preset; // 是否有预置位
    std::string algv_host;
    std::string algv_sync_camera_api;
    GC_BASE_CONFIG base_config;
    std::map<std::string, GC_CAMERA_CONFIG> camera_config_map;

    bool push_to_algv; // 是否推送到智慧件

    GC_APP_CONFIG() {
        if (AREA == "bda") {
            algv_host = "192.168.2.190:9909";
        } else {
            algv_host = "222.128.47.209:9909";
        }
        algv_sync_camera_api = "/camera/syncCameraBySN";
        push_to_algv = false;
    }
} GC_APP_CONFIG;

typedef struct GCStateImage {
    char img_path[100];
    char time_str[30];
    std::string camera_key;
} GCStateImage;

typedef struct GCTrackData {
    uint8_t* yuvForwardBuf;
    char img_path[100];
    std::vector<ObjBox> objBoxs;
    uint64_t pts;
    char time_str[30];
    std::string camera_key;

    // GCTrackData operator=(const GCTrackData& d)
    // {
    //     // 忽略 yuvForwardBuf
    //     yuvForwardBuf = nullptr;
    //     strcpy(img_path, d.img_path);
    //     objBoxs.assign(d.objBoxs.begin(), d.objBoxs.end());
    //     pts = d.pts;
    //     strcpy(time_str, d.time_str);

    //     return *this;
    // };

    void cloneExcludeYuvForwardBuf(GCTrackData& track_data)
    {
        track_data.yuvForwardBuf = nullptr;
        strcpy(track_data.img_path, img_path);
        track_data.objBoxs.assign(objBoxs.begin(), objBoxs.end());
        track_data.pts = pts;
        strcpy(track_data.time_str, time_str);
        track_data.camera_key = camera_key;
    }

} GCTrackData;

#endif